/*
 * C3.2.5.c
 *
 *  Created on: 2012/12/14
 *      Author: ming
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "TraceAst.h"
#include "rule_lib.h"

static const PSZ RuleID = "C3.2.5";

static const PSZ MESSAGE_ENG =
		"It is possible that memory region(%s) is reused for unrelated purposes";
static const PSZ MESSAGE_JPN = "メモリ領域(%s)を無関係な目的で再使用";
static const PSZ MESSAGE_CHS = "对象(%s)无关系的重复使用";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

static AST getNextStmt(AST stmt) {
	AST nextStmt = 0;
	if (stmt <= 0) {
		return 0;
	}
	nextStmt = CStatement.getNextStmt(stmt);
	while (nextStmt <= 0) {
		if (CStatement.isParentSymbol(stmt)) {
			return stmt;
		}
		if (!CStatement.isParentStmt(stmt)) {
			return 0;
		}
		stmt = CStatement.parent_GetStmt(stmt);
		if (stmt <= 0) {
			return 0;
		}
		nextStmt = CStatement.getNextStmt(stmt);
	}
	return nextStmt;
}

static AST getStmtFstExp(AST stmt) {
	AST exp = 0;
	AST tmpStmt = 0;
	if (stmt <= 0) {
		return 0;
	}
	if (CStatement.isExpression(stmt)) {
		exp = CStatement.expStatement_getExp(stmt);
	} else if (CStatement.isLabel(stmt)) {
		tmpStmt = CStatement.label_getStatement(stmt);
		if (tmpStmt) {
			exp = getStmtFstExp(tmpStmt);
		}
	} else if (CStatement.isCase(stmt)) {
		exp = CStatement.case_getConstExp(stmt);
	} else if (CStatement.isDefault(stmt)) {
		tmpStmt = CStatement.default_getStatement(stmt);
		if (tmpStmt) {
			exp = getStmtFstExp(tmpStmt);
		}
	} else if (CStatement.isBlock(stmt)) {
		tmpStmt = CStatement.block_getChain(stmt);
		if (tmpStmt) {
			exp = getStmtFstExp(tmpStmt);
		}
	} else if (CStatement.isIf(stmt)) {
		exp = CStatement.if_getCondExp(stmt);
	} else if (CStatement.isSwitch(stmt)) {
		exp = CStatement.switch_getCondExp(stmt);
	} else if (CStatement.isFor(stmt)) {
		exp = CStatement.for_getInitExp(stmt);
		if (exp <= 0) {
			exp = CStatement.for_getCondExp(stmt);
		}
		if (exp <= 0) {
			exp = CStatement.for_getIncExp(stmt);
		}
		if (exp <= 0) {
			tmpStmt = CStatement.for_getBody(stmt);
			if (tmpStmt) {
				exp = getStmtFstExp(tmpStmt);
			}
		}
	} else if (CStatement.isWhile(stmt)) {
		exp = CStatement.while_getCondExp(stmt);
	} else if (CStatement.isDo(stmt)) {
		tmpStmt = CStatement.do_getBody(stmt);
		if (tmpStmt) {
			exp = getStmtFstExp(tmpStmt);
		}
	} else if (CStatement.isGoto(stmt)) {
		tmpStmt = 0;
	} else if (CStatement.isBreak(stmt)) {
		tmpStmt = 0;
	} else if (CStatement.isContinue(stmt)) {
		tmpStmt = 0;
	} else if (CStatement.isReturn(stmt)) {
		exp = CStatement.return_getExp(stmt);
	} else if (CStatement.isDecl(stmt)) {
		return 0;
	}
	if (exp > 0) {
		return exp;
	}
	if (tmpStmt <= 0 || exp <= 0) {
		stmt = getNextStmt(stmt);
		if (CStatement.isParentSymbol(stmt)) {
			return 0;
		}
		exp = getStmtFstExp(stmt);
	}
	return exp;
}

AST getFuncSymbol(AST stmt) {
	if (stmt <= 0) {
		return 0;
	}
	while (!CStatement.isParentSymbol(stmt)) {
		if (!CStatement.isParentStmt(stmt)) {
			return 0;
		}
		stmt = CStatement.parent_GetStmt(stmt);
	}
	return CStatement.parent_GetSymbol(stmt);
}

static BOOL isInFunc(AST index, AST funcSymbol, BOOL bExp) {
	AST parent = 0;
	if (index <= 0 || funcSymbol <= 0) {
		return FALSE;
	}
	if (bExp) {
		if (CExpression.isParentStatement(index)) {
			parent = CExpression.parent_getStatement(index);
			return isInFunc(parent, funcSymbol, FALSE);
		} else if (CExpression.isParentExp(index)) {
			parent = CExpression.parent_getExp(index);
			return isInFunc(parent, funcSymbol, TRUE);;
		} else {
			return FALSE;
		}
	} else {
		if (CStatement.isParentSymbol(index)) {
			if (CStatement.parent_GetSymbol(index) == funcSymbol) {
				return TRUE;
			} else {
				return FALSE;
			}
		} else if (CStatement.isParentStmt(index)) {
			parent = CStatement.parent_GetStmt(index);
			return isInFunc(parent, funcSymbol, FALSE);
		} else if (CStatement.isParentExp(index)) {
			parent = CStatement.parent_GetExp(index);
			return isInFunc(parent, funcSymbol, TRUE);
		} else {
			return FALSE;
		}
	}
	return FALSE;
}

static BOOL isUsingLpcnt(AST exp, AST lpcntSymbol) {
	U4 expKind = CExpression.getOpKind(exp);
	if (CPostfixOpKind.isPostIncrement(expKind)
			|| CPostfixOpKind.isPostDecrement(expKind)
			|| CUnaryOpKind.isPreIncrement(expKind)
			|| CUnaryOpKind.isPreDecrement(expKind)
			|| CExpressionOpKind.isAssignmentOp(expKind)) {
		exp = CExpression.getLeftNode(exp);
		exp = getFstUnpChdExp(exp);
		expKind = CExpression.getOpKind(exp);
		if (CExpressionOpKind.isIdentifier(expKind)) {
			if (CExpression.getPrimarySymbol(exp) == lpcntSymbol) {
				return TRUE;
			}
		}
	}
	return FALSE;
}

static AST fstExpReused(AST forStmt) {
	AST exp = 0;
	AST nextStmt = getNextStmt(forStmt);
	AST lpcntSymbol = getForLoopCounter(forStmt);
	AST funcSymbol = getFuncSymbol(forStmt);
	if (nextStmt <= 0 || lpcntSymbol <= 0) {
		return 0;
	}
	if (CStatement.isParentSymbol(nextStmt)) {
		return 0;
	}
	exp = getStmtFstExp(nextStmt);
	if (exp <= 0) {
		return 0;
	}
	while (isInFunc(exp, funcSymbol, TRUE)) {
		if (isUsingLpcnt(exp, lpcntSymbol)) {
			return exp;
		}
		exp++;
	}
	return 0;
}

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(U2 kind, AST index, U4 event) {
	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	AST codeScp = 0;
	AST ngExp = 0;
	AST lpcntSymbol = 0;
	PSZ opName = NULL;

	if (kind != TR_KIND_STATEMENT) {
		return EXIT_SUCCESS;
	}

	if (!(event & EVT_STATEMENT)) {
		return EXIT_SUCCESS;
	}

	if (!CStatement.isFor(index)) {
		return EXIT_SUCCESS;
	}

	if (!isStandardFor(index)) {
		return EXIT_SUCCESS;
	}

	lpcntSymbol = getForLoopCounter(index);
	if (lpcntSymbol > 0) {
		opName = CSymbol.getName(lpcntSymbol);
	} else {
		opName = "";
	}

	ngExp = fstExpReused(index);
	if (ngExp > 0) {
		result = RULE_RESULT_NG;
	}

	if (result == RULE_RESULT_OK) {
		return EXIT_SUCCESS;
	}

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == index) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		codeScp = CExpression.getCodeScope(ngExp);
		newNode->indexInfo = index;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		sprintf(newNode->msgBuf_eng, MESSAGE_ENG, opName);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS, opName);
		sprintf(newNode->msgBuf_jpn, MESSAGE_JPN, opName);

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C3_2_5_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_cmd = NULL;
	newRuleMan->ruleMain_ast = RuleMain;
	newRuleMan->nextRule = NULL;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

